{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# 特異クラス\n",
    "\n",
    "* 特異クラス\n",
    "    * 指定したインスタンスだけに適用される特別なクラス\n",
    "        * 無名クラス(モジュールがインクルードされたときにそのメソッドが属するクラス)と同様にインタプリタ内部で使用するクラス\n",
    "            * ユーザに意識させたくないクラス\n",
    "        * 無名クラスと混同しないように気を付ける\n",
    "    * Silver試験の出題範囲外"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 特異クラスの性質\n",
    "\n",
    "* 特定のインスタンスに特別な性質を持たせたい\n",
    "    * 継承したクラスを書いて新たにインスタンス化するほどではない\n",
    "    * いちいちサブクラスを定義するのは面倒くさい\n",
    "        * このときは特異メソッドを使う\n",
    "* 特異メソッド\n",
    "    * インスタンスに直接定義したメソッド"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "foo1のクラスはFooのまま\n",
      "Foo\n",
      "foo2にmethodAは存在しない\n"
     ]
    },
    {
     "ename": "NoMethodError",
     "evalue": "undefined method `methodA' for #<Foo:0x007f083f08df88>",
     "output_type": "error",
     "traceback": [
      "\u001b[31mNoMethodError\u001b[0m: undefined method `methodA' for #<Foo:0x007f083f08df88>",
      "\u001b[37m(pry):139:in `<main>'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:355:in `eval'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:355:in `evaluate_ruby'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:323:in `handle_line'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:243:in `block (2 levels) in eval'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:242:in `catch'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:242:in `block in eval'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:241:in `catch'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:241:in `eval'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/backend.rb:66:in `eval'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/backend.rb:12:in `eval'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/kernel.rb:87:in `execute_request'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/kernel.rb:47:in `dispatch'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/kernel.rb:37:in `run'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/command.rb:70:in `run_kernel'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/command.rb:34:in `run'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/bin/iruby:5:in `<top (required)>'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/bin/iruby:22:in `load'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/bin/iruby:22:in `<main>'\u001b[0m"
     ]
    }
   ],
   "source": [
    "class Foo\n",
    "end\n",
    "foo1 = Foo.new\n",
    "foo2 = Foo.new\n",
    "\n",
    "### インスタンスに直接メソッドを定義 (特異メソッドの定義)\n",
    "def foo1.methodA\n",
    "  1 + 1\n",
    "end\n",
    "puts foo1.methodA\n",
    "puts \"foo1のクラスはFooのまま\"\n",
    "puts foo1.class\n",
    "\n",
    "puts \"foo2にmethodAは存在しない\"\n",
    "puts foo2.methodA\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "* 仕組み\n",
    "    1. 特異メソッドを定義する\n",
    "    1. Rubyインタプリタは特異クラスと呼ばれるクラスを作成した継承チェーンに組み込む\n",
    "        * foo1の特異クラスは「#foo1」と表現する\n",
    "        * 特異クラス#foo1のsuperclassはFooクラス (#foo < Foo のようなイメージ)\n",
    "        * foo1のクラスメソッドはFooクラスを指しているまま"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 特異クラスの参照\n",
    "\n",
    "* 特異クラスは通常ユーザに意識させたくないものであるが、参照は可能\n",
    "\n",
    "特異クラスの再オープン式\n",
    "``` ruby\n",
    "class << (対象オブジェクト)\n",
    "end\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "#<Class:#<Foo:0x007f083f15a088>>\n",
      "Foo\n"
     ]
    }
   ],
   "source": [
    "class Foo\n",
    "end\n",
    "foo1 = Foo.new\n",
    "\n",
    "SingletonClassA = class << foo1\n",
    "  self\n",
    "end\n",
    "\n",
    "puts SingletonClassA\n",
    "puts SingletonClassA.superclass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 特異クラスの再オープンとメソッドの定義\n",
    "\n",
    "` def <オブジェクト名>.<新たに定義するメソッド名> ... end ` でなくても特異クラスの再オープン式でメソッドを定義できる"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "4\n"
     ]
    }
   ],
   "source": [
    "class Foo\n",
    "end\n",
    "foo1 = Foo.new\n",
    "foo2 = Foo.new\n",
    "\n",
    "### defを使った特異メソッドの定義\n",
    "def foo1.methodA\n",
    "  1 + 1\n",
    "end\n",
    "\n",
    "### 特異クラスの再オープンと特異メソッド定義\n",
    "class << foo2\n",
    "  def methodB\n",
    "    2 + 2\n",
    "  end\n",
    "end\n",
    "\n",
    "puts foo1.methodA\n",
    "puts foo2.methodB"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## selfとメソッドの定義先\n",
    "\n",
    "* self\n",
    "    * 自分自身を示す特別なオブジェクト\n",
    "    * カレントオブジェクトとも呼ばれる\n",
    "        * カレントオブジェクトはselfキーワードでアクセスできる\n",
    "        * Rubyのコードはカレントオブジェクト内部で実行される\n",
    "    * selfの役割を担うオブジェクトは同時に複数存在しない\n",
    "        * 長時間その役割を担うオブジェクトも存在しない\n",
    "    * どこで使われるかによって示すオブジェクトが異なる\n",
    "        * クラス内部\n",
    "            * そのクラスを保持\n",
    "        * メソッド内部\n",
    "            * そのメソッドを実行するオブジェクトを保持\n",
    "            * メソッドを呼び出すときはレシーバがselfになる\n",
    "                * その時点で全てのインスタンス変数はselfのインスタンス変数になる\n",
    "            * レシーバを明示せずにメソッドを呼び出すと、全てselfに対するメソッド呼び出しになる\n",
    "                * 他のオブジェクトを明示してメソッドを呼び出すとそのオブジェクトがselfになる\n",
    "* レシーバ\n",
    "    * メソッドが実行されるオブジェクトのこと\n",
    "    * 「メソッドの受け手」という意味"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "クラス内部で呼び出したself\n",
      "#<C1:0x007f083e836588>\n",
      "クラスの中のインスタンスメソッド内部で呼び出したself(レシーバ)\n",
      "#<C1:0x007f083e836588>\n",
      "クラスのオブジェクトとレシーバは同じオブジェクト(そのクラスを参照している)\n",
      "true\n"
     ]
    }
   ],
   "source": [
    "class C1\n",
    "  self\n",
    "  def methodD\n",
    "    self\n",
    "  end\n",
    "end\n",
    "\n",
    "c1 = C1.new\n",
    "puts \"クラス内部で呼び出したself。クラスそのもの\"\n",
    "puts c1\n",
    "puts \"クラスの中のインスタンスメソッド内部で呼び出したself(レシーバ)\"\n",
    "puts c1.methodD\n",
    "puts \"クラスのオブジェクトとレシーバは同じオブジェクト(そのクラスを参照している)\"\n",
    "puts c1 == c1.methodD"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ClassA"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class ClassA\n",
    "  self   ### ClassAそのもの\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       ":methodB"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class ClassB\n",
    "  def methodB\n",
    "    self   ### methodB\n",
    "  end\n",
    "end "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "#<MyClass:0x007fa1e128b1f0 @var=11>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class MyClass\n",
    "  def testing_self\n",
    "    @var = 10          ### selfのインスタンス変数\n",
    "    my_method()　　　### self.my_method()と同じ\n",
    "    self                     ### selfへの参照を返す\n",
    "  end\n",
    "  \n",
    "  def my_method\n",
    "    @var = @var + 1\n",
    "  end\n",
    "end\n",
    "\n",
    "obj = MyClass.new\n",
    "obj.testing_self   ### この瞬間objがselfになる   @varはobjのインスタンス変数になり、my_methods()メソッドが呼び出されるのもobjになる"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### mainオブジェクト\n",
    "\n",
    "* メソッドを呼び出さない時のselfがmainオブジェクト\n",
    "    * トップレベルコンテキストとも呼ばれる\n",
    "            * コールスタックがトップレベルのときに居るオブジェクトだから\n",
    "* トップレベルとはどんなときか\n",
    "    * メソッドを呼び出していないときの状態\n",
    "    * 呼び出したメソッドが全て帰ってきたときの状態"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### privateについて\n",
    "\n",
    "* 4-6.MethodVisualization.ipynb\n",
    "* 5-1.ObjectClass.ipynb\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## メソッドのネスト\n",
    "\n",
    "* メソッドは定義したクラスに所属する\n",
    "* メソッドがネストした場合、内側のメソッドは外側のメソッドが定義されたクラスに定義される\n",
    "    * ただし、<font color=\"red\">外側のメソッドが実行されるまでは定義されない</font>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "まだmethodFは定義されていない\n",
      "[:methodE]\n",
      "methodEが実行されたためmethodFが定義された\n",
      "[:methodE, :methodF]\n"
     ]
    }
   ],
   "source": [
    "class C3\n",
    "  def methodE\n",
    "    def methodF\n",
    "    end\n",
    "  end\n",
    "end\n",
    "\n",
    "puts \"まだmethodFは定義されていない\"\n",
    "puts C3.instance_methods(false)\n",
    "C3.new.methodE\n",
    "puts \"methodEが実行されたためmethodFが定義された\"\n",
    "puts C3.instance_methods(false)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 特異クラスに対するMix-in\n",
    "\n",
    "* include\n",
    "    * クラスにモジュールをincludeする\n",
    "* extend\n",
    "    * 特異クラスへのMix-inで良く使用する\n",
    "    * includeよりシンプルに書ける\n",
    "* prepend\n",
    "    * includeと同様にモジュールのメソッドをクラスに取り込むためのメソッド\n",
    "    * クラスよりも先にモジュールのメソッド探索を行う\n",
    "    * 優先度\n",
    "        * prepend > (クラス内のメソッド) > include"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7\n",
      "7\n"
     ]
    }
   ],
   "source": [
    "module Bar\n",
    "  def methodA\n",
    "    3+4\n",
    "  end\n",
    "end\n",
    "\n",
    "class Foo\n",
    "end\n",
    "\n",
    "foo1 = Foo.new\n",
    "foo2 = Foo.new\n",
    "\n",
    "### includeを使ったMix-in\n",
    "class << foo1\n",
    "  include Bar\n",
    "end\n",
    "puts foo1.methodA\n",
    "\n",
    "### extendメソッドを使ったMix-in\n",
    "### foo2インスタンスでモジュールBarのメソッドmethodAを実行\n",
    "puts foo2.extend(Bar).methodA"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "prependはクラス内の度名のメソッドよりも優先される\n",
      "module M1 method1\n",
      "includeはクラス内に同名のメソッドがあればそちらを実行する\n",
      "class C2 method1\n",
      "ancestorsの表示順の違い\n",
      "[M1, C1, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]\n",
      "[C2, M1, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]\n"
     ]
    }
   ],
   "source": [
    "module M1\n",
    "  def method1; \"module M1 method1\"; end\n",
    "end\n",
    "\n",
    "class C1\n",
    "  prepend M1\n",
    "  def method1; \"class C1 method1\"; end\n",
    "end\n",
    "\n",
    "class C2\n",
    "  def method1; \"class C2 method1\"; end\n",
    "  include M1\n",
    "end\n",
    "\n",
    "puts \"prependはクラス内の度名のメソッドよりも優先される\"\n",
    "puts C1.new.method1\n",
    "\n",
    "puts \"includeはクラス内に同名のメソッドがあればそちらを実行する\"\n",
    "puts C2.new.method1\n",
    "\n",
    "puts \"ancestorsの表示順の違い\"\n",
    "puts C1.ancestors\n",
    "puts C2.ancestors"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## prepend内でsuperを呼び出す\n",
    "\n",
    "1. クラスC1内でprependでモジュールM1を読み込む\n",
    "1. M1内のsuperでスーパークラス(C1)を呼び出す\n",
    "1. C1のmethod1が実行される\n",
    "1. 続いてM1モジュール内のputsが実行される\n",
    "\n",
    "includeの場合はスーパークラスがObjectになり、Objectクラスにmethod1は存在しないためsuperでは何も実行されず、C1インスタンスメソッドmethod1のみ実行される"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[M1, C1, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]\n",
      "class C1\n",
      "module M1\n"
     ]
    }
   ],
   "source": [
    "module M1\n",
    "  def method1\n",
    "    super\n",
    "    puts \"module M1\"\n",
    "  end\n",
    "end\n",
    "\n",
    "class C1\n",
    "  prepend M1\n",
    "  #include M1\n",
    "  def method1\n",
    "    puts \"class C1\"\n",
    "  end\n",
    "end\n",
    "\n",
    "puts C1.ancestors\n",
    "C1.new.method1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## Refinements\n",
    "\n",
    "* Refinements\n",
    "    * クラスに対するメソッドの追加や変更が影響する範囲を絞れる\n",
    "    * Ruby2.1以降の機能\n",
    "    * メソッド\n",
    "        * refineメソッド\n",
    "            * 変更を加えるクラスを宣言\n",
    "        * usingメソッド\n",
    "            * 以降変更したメソッドの呼び出しが有効になる\n",
    "    * usingが有効になる範囲\n",
    "        * クラス・モジュールの定義の外で呼び出し\n",
    "            * ファイルの末尾まで\n",
    "        * クラス・モジュールの定義の中で呼び出し\n",
    "            * そのクラス・モジュール定義の末尾まで\n",
    "* 以下のプログラムの流れ\n",
    "    1. ClassAのfooメソッドが定義される\n",
    "        * `puts \"ClassA#foo\"`\n",
    "    1. ClassAのbarメソッドが定義される\n",
    "        * `foo`\n",
    "        * すなわち `puts \"ClassA#foo\"`\n",
    "    1. using ModuleAにより、以降ModuleAが有効になる\n",
    "        * fooメソッドはModuleAのfooメソッド `puts \"ClassA#foo in module ModuleA\"` になる\n",
    "    1. xにClassAのインスタンスが生成される\n",
    "    1. x.fooが実行される\n",
    "        * `ClassA#foo in module ModuleA`\n",
    "    1. x.barが実行される\n",
    "        * メソッドbar内でメソッドfooが実行される\n",
    "        * メソッドbarが定義されたのはusing ModuleAの前\n",
    "        * `ClassA#foo`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ClassA#foo in module ModuleA\n",
      "ClassA#foo\n",
      "[ClassA, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]\n"
     ]
    }
   ],
   "source": [
    "class ClassA\n",
    "  def foo\n",
    "    puts \"ClassA#foo\"\n",
    "  end\n",
    "  \n",
    "  def bar\n",
    "    foo\n",
    "  end\n",
    "end\n",
    "\n",
    "module ModuleA\n",
    "  refine ClassA do\n",
    "    def foo\n",
    "      puts \"ClassA#foo in module ModuleA\"\n",
    "    end\n",
    "  end\n",
    "end\n",
    "\n",
    "### ここより下のClassAのfooメソッドはrefineにより書き換えられる\n",
    "using ModuleA\n",
    "\n",
    "x = ClassA.new\n",
    "x.foo\n",
    "x.bar\n",
    "puts ClassA.ancestors"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Ruby 2.3.1",
   "language": "ruby",
   "name": "ruby"
  },
  "language_info": {
   "file_extension": ".rb",
   "mimetype": "application/x-ruby",
   "name": "ruby",
   "version": "2.3.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
